﻿// =====================================================================
// <copyright file="ConfigManager.cs" author="David R. Guindo">
// 	Copyright (C) 2013. All rights reserved.
// </copyright>
// =====================================================================

namespace AppBlock.Config
{
    using System;
    using System.Collections.Generic;
    using System.Configuration;
    using System.IO;
    using System.Linq;

    public static partial class ConfigManager
    {
        public static string LoadedConfig { get; set; }
        public static Dictionary<string, ConfigurationSection> CustomSections;

        private const string Configfilename = "*.Config";
        private static List<string> defaultSections;
        private const string Provider = "RsaProtectedConfigurationProvider";

        /// <summary>
        ///     Load te extended configuration.
        ///     Be careful with:
        ///     <list type="bullet">
        ///         <item>
        ///             <term>Placement</term>
        ///             <description>
        ///                 Depending on where you place your configuration file, your keys will last or not.
        ///                 The files in parent directories of the application have a lower priority than the files in the run
        ///                 directory.
        ///                 The files located in the Config folder have priority over the files in the run directory.
        ///             </description>
        ///         </item>
        ///         <item>
        ///             <term>Order</term>
        ///             <description>
        ///                 for parent configuration files, you can specify multiple files.
        ///                 If these files have the same keys and different values, the value will remain in the order of
        ///                 processing files
        ///             </description>
        ///         </item>
        ///     </list>
        /// </summary>
        /// <param name="parentLevel">directories levels for searched files with .config extension. By default is 0</param>
        /// <param name="environment">
        ///     Name of config subfolder for load custom configurations. By default is null (load MachineName
        ///     folder config)
        /// </param>
        /// <returns></returns>
        public static Dictionary<string, string> LoadConfigurations(int parentLevel = 0, string environment = null)
        {
            CustomSections = new Dictionary<string, ConfigurationSection>();
            if (environment == null) environment = Environment.MachineName;
            var values = new Dictionary<string, string>();

            //Load Parent configuration
            if (parentLevel > 0)
            {
                string level = string.Empty;
                while (parentLevel > 0)
                {
                    level += "..\\";
                    string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, level);

                    if (Directory.Exists(path))
                    {
                        ProcessConfigFiles(values, path, false);
                    }
                    else
                    {
                        break;
                    }
                    parentLevel--;
                }
            }

            //Load Environment configuration
            string environmentDir = "";
            try
            {
                environmentDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Config\\", environment);
                if (Directory.Exists(environmentDir))
                {
                    ProcessConfigFiles(values, environmentDir);
                }
            }
            catch (Exception ex)
            {
                throw new Exception(
                    string.Format("error accessing path '{0}'. See inner exception for more details.", environmentDir),
                    ex);
            }

            LoadedConfig = environment;
            return values;
        }

        public static object GetSection(string sectionName)
        {
            if (CustomSections.ContainsKey(sectionName)) return CustomSections[sectionName];
            return ConfigurationManager.GetSection(sectionName);
        }

        public static T GetSection<T>(T defaultValue = null) where T : ConfigurationSection
        {
            Type type = typeof(T);

            if (CustomSections != null)
            {
                if (CustomSections.Any(p => p.Value.GetType() == type))
                {
                    return (T)CustomSections.FirstOrDefault(p => p.Value.GetType() == typeof(T)).Value;
                }
            }
            
            var configMap = new ExeConfigurationFileMap { ExeConfigFilename = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile };
            var config = ConfigurationManager.OpenMappedExeConfiguration(configMap, ConfigurationUserLevel.None);

            if (config.Sections.Cast<ConfigurationSection>().Any(cs => cs.GetType() == type))
            {
                return (T)config.Sections.Cast<ConfigurationSection>().FirstOrDefault(cs => cs.GetType() == type);
            }

            return defaultValue;
        }
    }
}